1. XML에 대한 이해 (xml을 이해하고 있다면 스킵)
1.1 XML?
XML은 데이터를 저장하고 전달할 목적으로 만들어졌으며, 저장되는 데이터의 구조를 기술하기 위한 언어입니다. XML은
eXtensible Markup Language
의 약자로, 수많은 응용 분야에서 데이터를 저장하고 전달하는데 사용됩니다.가벼운 데이터를 저장하거나, 설정 파일 등에 사용되며
IoT에서는 SOAP 프로토콜을 주로 사용
하기 때문에 IoT환경에서도 많이 사용됩니다.1.2 XML 기본 구조
XML 선언
- XML태그를 사용하여 XML 문서임을 명시합니다.
XML 구조
XML은 기본적으로
트리 형식
입니다. 조상 노드, 부모노드, 자식 노드 형태로 계층적으로 형성됩니다. 조상 노드는 부모 요소를 포함해 계층적으로 현재 요소보다 상위에 존재하는 모든 요소를 가리킵니다.- 여기서
shop
이 루트(조상) 요소입니다.
- 여기서
city
와type
은 속성 값 입니다.
XML 기본 규약
- xml은
종료 태그를 항상 가져야 합니다.
xml태그는 대소문자를 구분합니다.
- xml에서 속성값은 반드시 따옴표로 감싸야 합니다.
- xml에서는 띄어쓰기를 인식합니다.
1.3 DTD
문서 타입 정의(DTD)는 XML 문서의 구조 및 해당 문서에서 사용할 수 있는 적법한 요소와 속성을 정의합니다.
DTD는 엔티티를 정의할 수 있으며, DTD를 사용하여 새로운 XML 문서의 구조를 정의함으로써 새로운 문서 타입을 만들 수 있습니다. 또한 응용 프로그램은
DTD의 정의에 따라 XML문서의 구문 및 구조에 대한 유효성을 검사할 수 있습니다.
(파싱).dtd
처럼 별도의 파일에 저장하고 호출할 수도 있으며, xml 파일 내부에서 선언되기도 합니다. DTD 구조
기본적으로 DTD의 시작은
<!DOCTYPE 루트 [ 엘리먼트, 엔티티 등등 ] >
형태로 선언됩니다.DTD의 구성 요소
🔎 요소 (Elements)
- XML 문서를 구성하는 가장 중요한 구성 요소입니다.
🔎 속성 (attributes)
- 속성은 요소에 대한 추가적인 정보를 제공하며, 해당 요소의 특징을 정의합니다.
🔎 엔티티 (entities)
XXE에서 가장 중요한 부분입니다.
XML은 예약되어 있는 다섯개의 특별한 기호가 있습니다. 이렇게 예약된 기호는 XML파서가 전혀 다른 의미로 해석합니다. 이렇게
예약된 기호를 기존에 사용하던 의미 그대로 사용하기 위해서 만든 문자셋을 엔티티라고 합니다.
여기서 예약된 기호는 다음과 같습니다.
&
→ &
<
→ <
>
→ >
"
→ “
'
→ '
엔티티는 다음과 같이 선언합니다.
내부 엔티티
: XML내에 있는 엔티티를 조회할 때 사용됩니다.
외부 엔티티
: XML 외부, 즉 URI에서 데이터를 가져와서 해당 값을 참조합니다.
예시로 다음과 같이 외부 파일을 불러올 수 있습니다.
그리고 불러온 파일을 참조할 수 있습니다.
그 외 다른 구성 요소 중
PCDATA
는 XML 파서에 의해 분석될 문자 데이터를 의미하고, CDATA
는 XML 파서가 분석하지 않는 문자 데이터를 의미합니다.2. XXE (XML External Entities)
2.1 XXE
XXE는 말 그대로
XML에서 외부 엔티티를 선언할 수 있을 때
발생하는 취약점 입니다. 앞서 설명한 것 중, 외부 파일을 XML데이터로 가져와 엔티티로 사용할 수 있다는 것을 알았습니다. 이때 공격자가 원하는 파일을 가져올 수 있을 경우 발생하는 취약점 입니다.2.2 XXE 발생 예시
XXE가 발생하려면 일단 서버 측에서
XML 파서
가 동작하여야 합니다. 프론트단에서 XML형태로 데이터를 보내거나, 미들웨어에서 XML로 조회를 하는 기능이 있는 경우 테스트 해볼 수 있습니다.또한 XXE 공격의 결과가 담긴 응답이 오거나, 응답이 오지 않는 경우로 나눌 수 있습니다. 다음은 두가지 예시에 대한 XXE 공격의 차이 입니다.
XXE 공격이 응답에 담겨오는 경우
어떤 물건의 남은 재고를 조회하는 웹 애플리케이션 기능이 존재합니다.
이때 XML로 서버에게 요청을 하고, 서버는 XML파서를 사용하여 구문을 분석하고, 결과를 반환 하게 됩니다. 여기서 응답을 잘 살펴 보아야 합니다.
productId
에 foo
라는 잘못된 값을 주고. 응답을 확인해 보겠습니다.productId
에foo
라는 잘못된 값을 주었을 때, 웹 서버가 입력한 값을 반환하는 로직을 가지고 있다는 것을 확인할 수 있습니다.
- 만약
서버 내부 파일을 읽어 XML에 포함시키고
,해당 값을 응답 값에 반환시킬 수 있다면
어떻게 될까요?
XXE 공격을 통해
/etc/passwd
파일을 읽어와 엔티티로 선언하고, 해당 값을 Response에 담길 수 있도록 요청하였습니다.- 서버의 계정 정보를 유출할 수 있는 취약점을 확인할 수 있습니다.
추가적으로, 요청을 받는 서버가 내부망에 존재할 경우,
SSRF
공격 또한 기대해 볼 수 있습니다또한, 엔티티에는 두가지 타입이 있습니다.
🔎 일반 엔티티
- 기본적으로 사용되며, XML문서에서 텍스트나 다른 데이터를 포함합니다.
🔎 매개 변수 엔티티
- 매개변수 엔티티는 DTD 내에서 사용되며, 엔티티의 정의나 구조를 모듈화하고 재사용 할 수 있게 합니다.
% 엔티티
형식으로 선언해 주고 Doctype 내에서% 엔티티
형식으로 호출해 주면 됩니다.
&이 필터링
되어 있을 때 사용할 수 있습니다.
XXE 공격의 성공 여부가 요청 응답에 담기지 않는 경우
XML로 데이터를 보내는게 확인되거나, XML파서가 존재하는 것이 확인된 서버지만, 보통 서버는 사용자에게
에러 정보 혹은 사용자의 입력값
을 잘 반환하지 않습니다.이때는 Blind 형식으로 XXE를 진행하여야 합니다.
예를 들어, 웹 훅 사이트를 하나 만들어 놓고, 해당 웹훅 사이트의 데이터를 XXE를 통해 참조하게 되면
웹훅 사이트로 내부 서버가 접근하게 됩니다.
- 웹훅 주소를 엔티티로 선언하면,
XML이 파싱될 때 웹훅 주소로 요청을 하게 됩니다
만약 웹훅 주소에서 요청이 와있다면, XXE 공격이 성공하는 사이트라고 판단할 수 있습니다.
또한 내부 파일을 읽어 웹훅 사이트의 파라미터로 값을 GET방식으로 전달 할 수 있습니다.
다음은 예시 코드입니다.
Out-Of-Bound(oob) 공격에서의 싱글라인과 멀티라인
- 일반적으로
file entity
에서 원하는 정보를 가져와서웹훅 서버에 파라미터 형식으로 정보를 전달합니다.
근데 이 경우,/etc/passwd
같은 멀티라인 파일을 보내게 되면 첫 개행 전까지 데이터만 가져오게 됩니다. 이를 해결하기 위해서ftp 서버로 전송
하는 방식과에러메세지에 file entity 요소 담기
방법이 있습니다.
👻 에러 메세지를 이용한 멀티라인 출력 방법은 하단에 나와있습니다.
3. 추가적인 Bypass
3.1 &을 사할 수 없을 경우
파라미터 엔티티
를 사용하면 됩니다.이때 XML 파서 혹은 다양한 Black list, 길이 제한 등이 있기 때문에 다양한 환경에서 선택하여 테스트 해보아야 합니다.
인코딩 형식의 XXE
➡️ 웹훅 서버에 공격할 XXE 구문을 파일로 지정합니다.
- 해당 값을 XML 파서가 읽었을 때 구문이 실행될 수 있도록 엔티티만 작성합니다.
- 순서는
file 엔티티 선언
→%stack;
→%exfil 엔티티 정의
→%exfil;
→exfil 엔티티 실행
→%file 엔티티의 내용을 보냄
형태 입니다.
- 이렇게 이중 구문을 사용해준 이유는 xml 파서에 따라
file
이 해석이 되지 않을 수 있기 때문입니다.(그렇다면 webhook.server?file=%file 형태로 보내집니다.)
- 엔티티를
동적 생성
한다고 생각하시면 됩니다.
➡️ XXE를 수행할 서버에
웹훅 서버에서 데이터를 불러오는 엔티티
를 보냅니다.- 이렇게 되면 외부 ENTITY 호출을 통해 엔티티를 선언하고 동적으로 구성된 XXE 페이로드가 실행되게 됩니다.
3.2 에러 메세지를 이용한 XXE
만약 XXE 파서가
XML을 파싱하는 부분에서 에러 내용을 Response에 담을 경우
발생하는 취약점 입니다. 페이로드는 다음과 같습니다.순서
- file 엔티티 정의
- stack 엔티티 정의 및 호출 → file 엔티티가 WTF/ 이후 경로에 담김 → xxe 엔티티 정의
- xxe 엔티티 호출
file:///WTF
경로가 존재하지 않기 때문에 에러 발생
- XML 파서는 에러가 발생한 부분을 반환해야 하기 때문에
file 엔티티 내용을 반환하게 됨
예시 (해당 페이로드가 담긴 외부 엔티티를 가져오는 모습)
이 공격은 웹 훅 서버에서 데이터를 leak 하는 대신, 웹 요청에서 에러를 발생시키며 공격자가 직접 공격 결과를 확인할 수 있습니다.
- XML 파싱 부분에 어디서 에러가 발생하였는지 Return 하면서 취약점이 발생합니다. (여기서는 없는 경로를 요청했기 때문에 없는 경로 이후의 값들을 전부 다 노출했네요)
- 특이점으론, URL 요청이 아니기 때문에 XXE를 통해 가져오고자 하는 파일이 멀티라인이여도 전부 다 가져올 수 있습니다.
3.3 XInclude 를 사용한 XXE
특정 어플리케이션은
클라이언트의 데이터를 받아 xml파일에 저장하기도 합니다.
(구식이지만 생각보다 JAVA 어플리케이션에서 사용자의 정보를 저장할 때 이런식으로 저장하기도 합니다.) 그리고 해당 문서를 파싱합니다.
XInclude?
- XML 문서 내에서
<xi:include>
요소를 사용하여 다른 XML 문서나 문서의 특정 부분을 포함할 수 있습니다.
href
속성을 통해로컬 파일 경로 혹은 웹상의 URL을 불러올 수 있습니다
parse
속성을 통해href
로 가져온 데이터를 어떻게 파싱할 것인지 지정할 수 있습니다.
예시
productId
와 storeId
를 받아 물건의 개수를 조회하는 애플리케이션이 존재합니다.해당 애플리케이션은
없는 ID
를 조회할 경우 입력 문자열을 반환합니다
현재 일반적인 ContentType인
x-www-form-urlencoded
로 보내고 있습니다. 하지만 파서의 종류는 예측할 수 없기에 XXE 테스트를 위해서 엔티티 값을 집어넣어 봅니다. %26은 &
- 엔티티는 보안적인 이슈로 허용되지 않는다고 합니다.
- 따라서 해당 서버에 XML파서가 동작하는 것을 알 수 있습니다.
- (혹은 보안 로직이 존재하거나요)
따라서 XXE 공격을 위해 Content-Type을 변경해서 시도해 볼 수 있습니다.
- 하지만 서버에서 파라미터를 인식하지 못하고 있다고 반환됩니다.
- XML엔티티는 제한하면서, Content-Type은 해석하지 못한다는 것은
서버에서 받은 데이터를 XML문서에 데이터를 저장하고 있거나, SOAP 프로토콜 등을 사용하여 또 다른 백앤드 서버와 통신하고 있을 수 있음
을 내포합니다..
따라서
여기서는 XInclude를 사용한 XXE를 시도해 볼 수 있습니다.
- XInclude를 사용하여
/etc/passwd
파일을 가져오는 것을 볼 수 있습니다.
3.4 파일 업로드에서 발생하는 XXE
파일 업로드가 가능한 곳에서
svg
파일을 업로드 할 수 있을 경우 시도해 볼 수 있는 취약점 입니다. svg파일은 xml 포멧을 사용하기 때문에
만약 svg 파일이 업로드가 가능하다면 XXE 구문을 삽입할 수 있습니다.예시
SVG 파일을 올릴 수 있는 웹페이지가 있습니다.
SVG는 XML포멧을 사용하기 때문에 여기에 엔티티를 선언하고 이미지에 포함시킬 수 있습니다.
짜잔! 파일 업로드도 XXE 공격이 가능합니다. 또 다른 예시로
PDF
같은 경우도 XXE 공격을 할 수 있기 때문에 다양한 방법으로 어택 벡터를 공략할 수 있습니다.4. 보안 조치
4.1 외부 엔티티 제한
외부 엔티티 선언을 불가능하게 사용하면 됩니다. 또한 xml파서가 필요하지 않은 경우 체크해볼 필요가 있습니다.
4.2 파일 업로드 검사
SVG, PDF 등 XML과 연관된 파일을 업로드 할 때에는 적절한 보안 조치를 수행하거나, 파일의 확장자를 제한하는 법도 존재합니다.